package dosgi.core.util;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicReference;

import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogService;

import dbconnector.service.IServiceInfo;
import dbconnector.service.IServiceRegisterManager;

public class DatabaseProxy {

	private LogService logger = LogHelper.getLogger();
	
	private Timer databaseUpdater;
	private AtomicReference<String> queueName = new AtomicReference<String>();
	private AtomicReference<IServiceRegisterManager> databaseManager = new AtomicReference<IServiceRegisterManager>();
	private Map<String, IServiceInfo> notPersistentServices = new HashMap<String, IServiceInfo>();
	private Map<String, ExtendedServiceInfo> persistentServices = new HashMap<String, ExtendedServiceInfo>();
	private Map<String, ExtendedServiceInfo> toRemoveServices = new HashMap<String, ExtendedServiceInfo>();

	public IServiceRegisterManager getDatabaseManager() {
		return databaseManager.get();
	}

	public void setDatabaseManager(IServiceRegisterManager databaseManagerRef) {
		databaseManager.set(databaseManagerRef);
		
		modifyDatabaseTimer();
	}
	
	public void setQueueName(String queueNameString) {
		queueName.set(queueNameString);
	}
	
	public synchronized void registerService(ServiceReference<?> serviceReference, IServiceInfo serviceInfo) {
		IServiceRegisterManager database = getDatabaseManager();
		
		notPersistentServices.put(serviceReference.toString(), serviceInfo);
		
		persistServices(database);
	}
		
	private void persistServices(IServiceRegisterManager database) {
		if (database != null && queueName.get() != null) {
			for (String reference : notPersistentServices.keySet()) {
				IServiceInfo serviceToRegister = notPersistentServices.get(reference);
				serviceToRegister.setQueueName(queueName.get());
				
				String uid = database.registerService(serviceToRegister);
				
				if (uid != null) {
					logger.log(LogService.LOG_INFO, "Service " + serviceToRegister.getServiceName() + " has been successfully registered");
					
					persistentServices.put(reference.toString(), new ExtendedServiceInfo(uid, serviceToRegister));
					notPersistentServices.remove(reference.toString());
				} else {
					logger.log(LogService.LOG_WARNING, "Service " + serviceToRegister.getServiceName() + " has not been successfully registered");
				}
			}
		}
	}
	
	public synchronized void unregisterService(ServiceReference<?> serviceReference) {
		IServiceRegisterManager database = getDatabaseManager();
		
		ExtendedServiceInfo serviceInfo = persistentServices.get(serviceReference.toString());
		if (serviceInfo != null) {
			toRemoveServices.put(serviceReference.toString(), serviceInfo);
			
			if (database != null) {
				for (String reference : toRemoveServices.keySet()) {
					serviceInfo = toRemoveServices.get(reference);
					
					database.unregisterService(serviceInfo.getUid());
					
					persistentServices.remove(reference);
					toRemoveServices.remove(reference);
					
					logger.log(LogService.LOG_INFO, "Service " + serviceInfo.getServiceInfo().getServiceName() + " has been successfully unregistered");
				}
			}
		} else {
			notPersistentServices.remove(serviceReference.toString());
		}
	}
	
	public synchronized void updateServices() {
		IServiceRegisterManager database = getDatabaseManager();
		
		if (database != null) {
			List<String> idsToCheck = getRegisteredServicesIDs();
			
			List<String> idsToReregister = database.updateServicesList(idsToCheck);
			
			moveToNotPersisted(idsToReregister);
			
			persistServices(database);
		}
	}

	private List<String> getRegisteredServicesIDs() {
		List<String> ids = new LinkedList<String>();
		
		for (ExtendedServiceInfo serviceInfo : persistentServices.values()) {
			ids.add(serviceInfo.getUid());
		}
		
		return ids;
	}
	
	private void moveToNotPersisted(List<String> idsToReregister) {
		for (String reference : persistentServices.keySet()) {
			ExtendedServiceInfo serviceInfo = persistentServices.get(reference);
			
			if (idsToReregister.contains(serviceInfo.getUid())) {
				notPersistentServices.put(reference, serviceInfo.getServiceInfo());
				persistentServices.remove(reference);
			}
		}
	}
	
	public List<IServiceInfo> getService(String name) {
		IServiceRegisterManager database = getDatabaseManager();
		
		if (database != null) {
			return database.getService(name);
		}
		
		return null; // TODO
	}
	
	private void modifyDatabaseTimer() {
		IServiceRegisterManager database = getDatabaseManager();
		
		if (databaseUpdater != null) {
			databaseUpdater.cancel();
		}
		
		if (database != null) {	
			databaseUpdater = new Timer();
			databaseUpdater.schedule(new TimerTask() {
				@Override
				public void run() {
					updateServices();
				}
			}, 0, 20000);
			
			if (logger != null) {
				logger.log(LogService.LOG_INFO, "Database timer set");
			}
		}
	}
}
